#include"http_conn.h"
#include"threadpool.h"
#include"memorypool.h"

#include<list>
#include<map>
#include<vector>
#include<string>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>
#include<stdlib.h>
#include<cassert>
#include<sys/epoll.h>


#define MAX_FD 65536
#define MAX_EVENT_NUMBER 10000


extern int setnonblocking(int fd);
extern void addfd(int epollfd,int fd,bool one_shot);
extern void removefd(int epollfd,int fd);
extern void modfd(int epollfd,int fd,int ev,bool lis);

int pipefd[2];
std::list<http_conn*> time_lis;
int epollfd;
http_conn *users;
threadpool* Thpool;
std::map<std::string, file_info*> filehead;
memorypool* MemPool;

void sig_handler(int sig)
{
    int save_errno=errno;
    int msg=sig;
    send(pipefd[1],(char*)&msg,1,0);
    errno=save_errno;
}

//设置信号处理
void addsig(int sig)
{
    struct sigaction sa;
    memset(&sa,'\0',sizeof(sa));
    sa.sa_handler=sig_handler;
    sa.sa_flags|=SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig,&sa,NULL)!=-1);
}

void timer_handler()
{
    printf("\ntime_tick\n");
    std::list<http_conn*>::iterator it=time_lis.begin();
    std::list<http_conn*>::iterator its=it;
    time_t cur=time(NULL);
    for(;it!=time_lis.end();)
    {
        if(cur>(*its)->time_out)
        {
            it++;
            (*its)->close_conn();
            time_lis.erase(its);
            its=it;
            continue;
        }
        it++;
    }
    alarm(5);
}


void show_error(int connfd,const char* info)
{
    printf("%s",info);
    send(connfd,info,strlen(info),0);
    close(connfd);
}

int main(int argc,char* argv[])
{
    if(argc<=2)
        printf("usage:%s ip_address port_number\n",basename(argv[0]));
    const char* ip=argv[1];
    int port=atoi(argv[2]);

    addsig(SIGALRM);
    addsig(SIGTERM);
    

    socketpair(PF_UNIX,SOCK_STREAM,0,pipefd);
    setnonblocking(pipefd[1]);

    //登陆数据库
    //SQL.connectSQL("localhost","shio","qweasd","myweb");

    
    {
        struct stat file_stat;
        std::vector<std::string> temp;
        temp.push_back("./http/index.html");
        temp.push_back("./http/style.css");
        temp.push_back("./http/myweb/index.html");
        temp.push_back("./http/myweb/favicon.ico");
        temp.push_back("./http/myweb/apple-touch-icon.png");
        temp.push_back("./http/myweb/img/me.jpg");
        temp.push_back("./http/favicon.ico");
        for(auto sl:temp)
        {
            if(!stat(sl.c_str(),&file_stat))
            {
                file_info* test = new file_info;
                test->wherefile = static_cast<char*>(malloc(file_stat.st_size));
                test->filesize = file_stat.st_size;
                filehead[sl] = test;
                int fd=open(sl.c_str(),O_RDONLY);
                read(fd,filehead[sl]->wherefile,filehead[sl]->filesize);
                close(fd);
            }
        }
    }
    
    MemPool = new memorypool;

    Thpool = new threadpool(2, 10000);
    Thpool->start();

    users=new http_conn[MAX_FD];
    assert(users);
    int user_count=0;

    int listenfd=socket(PF_INET,SOCK_STREAM,0);
    assert(listenfd>=0);
    struct linger tmp={1,0};
    setsockopt(listenfd,SOL_SOCKET,SO_LINGER,&tmp,sizeof(tmp));

    int ret=0;
    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family=AF_INET;
    inet_pton(AF_INET,ip,&address.sin_addr);
    address.sin_port=htons(port);

    ret=bind(listenfd,(struct sockaddr*)&address,sizeof(address));
    assert(ret>=0);
    ret=listen(listenfd,10);
    assert(ret>=0);

    epoll_event events[MAX_EVENT_NUMBER];
    epollfd=epoll_create(10);
    assert(epollfd!=-1);
    addfd(epollfd,listenfd,false);
    addfd(epollfd,pipefd[0],false);
    http_conn::m_epollfd=epollfd;

    alarm(5);
    while(true)
    {
        int number=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
        if((number<0)&&(errno!=EINTR))
        {
            printf("epoll failure\n");
            break;
        }

        for(int i=0;i<number;i++)
        {
            int sockfd=events[i].data.fd;
            if(sockfd==listenfd)
            {
                struct sockaddr_in client_address;
                socklen_t client_addrlength=sizeof(client_address);
                int connfd=accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);
                if(connfd<0)
                    printf("errno is:%d\n",errno);
                modfd(epollfd,listenfd,EPOLLIN,false);
                if(http_conn::m_user_count>=MAX_FD)
                {
                    show_error(connfd,"Internal server busy");
                    continue;
                }
                users[connfd].init(connfd,client_address);
                time_lis.push_back(users+connfd);
            }
            else if(events[i].events&(EPOLLRDHUP|EPOLLHUP|EPOLLERR))
                users[sockfd].close_conn();

            else if((sockfd!=pipefd[0])&&(events[i].events&EPOLLIN))
                Thpool->add(std::bind(&http_conn::process, users+sockfd));

            else if((sockfd==pipefd[0])&&(events[i].events&EPOLLIN))
                timer_handler();
        }
    }
    close(epollfd);
    close(listenfd);
    delete [] users;
    delete Thpool;
    delete MemPool;
    return 0;
}